// Code adopted from Internet
// http://celestialcoding.com/index.php?topic=1671.0
// Modified

#include "LoadImg.h"


ImageBMP::ImageBMP(){
   /*
      Zero everything out
   */
   mBitmap   = NULL;
   mWidth   = 0;
   mHeight   = 0;
   mStride   = 0;
   mType   = 0;

   GdiFlush();
}
ImageBMP::~ImageBMP(){
   FreeBMP();
}
void ImageBMP::FreeBMP(){
   if(mBitmap)
      DeleteObject(mBitmap);

   mBitmap   = NULL;
   mWidth   = 0;
   mHeight   = 0;
   mStride = 0;
   mType   = 0;
   imgData   = NULL;
}
unsigned char* ImageBMP::GetImageData(int which_line){
   return (imgData + mStride * which_line);
}
bool ImageBMP::SizeImg(int w, int h, int channels){
   /*
      To begin with, we clear the memory of the image incase anything
         was already loaded in. We then create a temporary device
         context and initialize our width, height, and type variables.
         We also initialize our stride variable, but read further down for
         more information on that.
   */
   FreeBMP();
   HDC tmpDC = CreateCompatibleDC(NULL);
   if(!tmpDC){
      MessageBox(NULL, "CreateCompatibleDC() failed!", "Error SizeImg()", MB_OK);
      return false;
   }

   mWidth   = w;
   mHeight   = h;
   mType   = channels;

   /*
      In windows and most other OS's. Some things are far easier
         to accomplish when the data is dword aligned. dword = 2 words
         or 2 words = 4 bytes (Some asm knowledge there). Anyhow, this
         helps speed up drawing to the screen, we we get the true stride,
         then modify it until it is evenly divided by 4. IF you ever
         wondered when the hell you would use the modulus symbol, here it
         is!
   */
   mStride = mWidth * mType;
   while((mStride % 4) != 0)
      mStride++;

   BITMAPINFO bInfo = {0};

   /*
      We set up the bitmap data here for when we create the image itself
         from the file data. biSize is the size of the header. WE just pass
         in the BitmapInfoHeader struct for this. biWidth and biHeight should
         not need explanation. biPlanes will always be 1 regardless of what type
         of image you load. Even MSDN says it MUST be 1. Next we have biBitCount
         which is just the number of channels we have multiplied by 8 (8 bits per byte).
         If you wanted to be an asshole to someone, you could always ask "Hey, can you
            save that in 3 byte format?" (24bit)
         Anyways, moving along... We come across biCompression. Since no compression
         should have happened to the image we are loading, we just tell it no compression
         occured to the image data. Finally, biClrUsed is just a indicies tracker for images
         in the 8 bit range for color platelets. We don't need it though so set it to 0.
   */
   bInfo.bmiHeader.biSize         = sizeof BITMAPINFOHEADER;
   bInfo.bmiHeader.biWidth         = mWidth;
   bInfo.bmiHeader.biHeight      = mHeight;
   bInfo.bmiHeader.biPlanes      = 1;         // This will always be 1
   bInfo.bmiHeader.biBitCount      = mType * 8;   // 3 channels = 24, 4 = 32
   bInfo.bmiHeader.biCompression   = BI_RGB;      // No compression
   bInfo.bmiHeader.biClrUsed      = 0;         // Always 0 when working with 24/32 bit bmps


   /*
      Finally, we actually create the image to store our data into. After creating it, we
         check to see if it finished properly, then we delete the temporary device
         context created earlier. Do a final check to make sure all is okay, then return true!
   */
   mBitmap = CreateDIBSection(tmpDC, &bInfo, DIB_RGB_COLORS, (void**)&imgData, 0, 0);

   DeleteDC(tmpDC);
   if(!mBitmap){
      MessageBox(NULL, "CreateDIBSection() failed!", "Error SizeImg()", MB_OK);
      return false;
   }
   return true;
}
bool ImageBMP::LoadBMP(const char *filename){
   /*
      To start off, we erase any memory or garbage memory that might be in the
         bitmap class already. After that we proceed to check and ensure a filename was
         ACTUALLY passed into the function, if it was, we open the file and proceed to
         read the data into our holder and then into OpenGL.
   */
   FreeBMP();
   if(!filename){
      MessageBox(NULL, "Bad file name!", "Error LoadBMP()", MB_OK);
      return false;
   }

   FILE * img = NULL;
   fopen_s(&img, filename, "rb");   // Notice we read the file in  byte by byte. 'rb' means Read Byte mode.   

   if(!img){
      MessageBox(NULL, "fopen() failed!", "Error LoadBMP()", MB_OK);
      return false;
   }

   // Stores information on the bitmap header when we load it in.
   BITMAPFILEHEADER bHeader;

   if(!fread(&bHeader, sizeof bHeader, 1, img)){
      MessageBox(NULL, "fread() [BITMAPFILEHEADER] failed!", "Error LoadBMP()", MB_OK);
      return false;
   }

   /*
      Here, we check to make sure this is ACTUALLY a bmp file. You can rename a .jpg
         to .bmp, but the compression still exists inside it. If we tried to load it
         while it was still compressed, bad things will happen. We check to avoid that
         bad situation. Note that memcmp returns true if the memory does NOT match. Don't
         ask me why.
   */
   if(memcmp(&bHeader.bfType, "BM", 2)){
      MessageBox(NULL, "memcmp() detected false file type!", "Error LoadBMP()", MB_OK);
      fclose(img); // Close the file since it is the wrong filetype.
      return false;
   }

   // This contains information on the bitmap itself
   BITMAPINFOHEADER bInfo;

   // Read in the info for the bitmap.
   if(!fread(&bInfo, sizeof(BITMAPINFOHEADER), 1, img))
   {
      MessageBox(NULL, "fread() [BITMAPINFOHEADER] failed!", "Error LoadBMP()", MB_OK);
      fclose(img);
      return false;
   }

   if((bInfo.biBitCount != 24) && (bInfo.biBitCount != 32)){
      /*
         Here, we checked if the image was 24 or 32 bits. If it is neither, then we just return false after
            closing the file. This loader is currently unable to load monochrome, or 8 bit bmp images. We
            will simply stick to 24 or 32.
      */
      MessageBox(NULL, "Not a valid 24 | 32 bit image type!", "Error LoadBMP()", MB_OK);
      fclose(img);
      return false;
   }

   /*
      After getting all the information from the header, we then set the image data up
         and prepare it for loading in the actual image data itself. That is done
         with the function call we created earlier in the class. We pass in the height
         and the width. To send in the number of channels, it is the bit count / 8 (8 bits per byte),
         and 3 bytes is the number of channels. Or 4...
   */
   if(!SizeImg(bInfo.biWidth, bInfo.biHeight, bInfo.biBitCount/8)){
      MessageBox(NULL, "Size() failed!", "Error LoadBMP()", MB_OK);
      fclose(img);
      return false;
   }

   /*
      What is about to happen might look confusing but it is very straight forward!
         First, we need to find out how many bits are on each line in the image.
         Second, we need to find out how many of those bytes are padded since we want to avoid reading those in.
         Finally, we read the data into the image handler.
   */
   unsigned int bytesPerLine   = mWidth * mType;         // Get the number of bytes per line
   unsigned int padCount      = mStride - bytesPerLine;   // Get the amount of padding involved.

   // Loop through and read the data in
   for(int i=0; i<mHeight; i++){
      /*
         i is the line number in the image we are on. In this case, i
            will start at 0 being the top of the file. From each line, we need
            to relocate our position
      */
      unsigned char* pos = GetImageData(i);         // Relocate our position

      /*
         Read in the image data
      */
      if(!fread(pos, bytesPerLine, 1, img)){
         MessageBox(NULL, "fread() at ImageData failed!", "Error LoadBMP()", MB_OK);
         fclose(img);
         return false;
      }

      /*
         Skip over padding, remember that fseek returns 0 on succes, and negative values
            for errors.
      */
      if(fseek(img, 0, SEEK_CUR)){
         MessageBox(NULL, "fread() at padding failed!", "Error LoadBMP()", MB_OK);
         fclose(img);
         return false;
      }
   }

   // Loaded the image successfully, we now just do what we want with the image data
   fclose(img);
   return true;
} 

bool ImageBMP::LoadRAW(const char *filename){
   /*
      To start off, we erase any memory or garbage memory that might be in the
         bitmap class already. After that we proceed to check and ensure a filename was
         ACTUALLY passed into the function, if it was, we open the file and proceed to
         read the data into our holder and then into OpenGL.
   */
   FreeBMP();
   if(!filename){
      MessageBox(NULL, "Bad file name!", "Error LoadRAW()", MB_OK);
      return false;
   }

   FILE * img = NULL;
   fopen_s(&img, filename, "rb");   // Notice we read the file in  byte by byte. 'rb' means Read Byte mode.   

   if(!img){
      MessageBox(NULL, "fopen() failed!", "Error LoadRAW()", MB_OK);
      return false;
   }

   long w,h;

   if(!fread(&w, 4, 1, img )|| !fread(&h, 4, 1, img)){
      MessageBox(NULL, "fread() w,h failed!", "Error LoadRAW()", MB_OK);
      fclose(img);
      return false;
   }

   /*
      After getting all the information from the header, we then set the image data up
         and prepare it for loading in the actual image data itself. That is done
         with the function call we created earlier in the class. We pass in the height
         and the width. To send in the number of channels, it is the bit count / 8 (8 bits per byte),
         and 3 bytes is the number of channels. Or 4...
   */
   if(!SizeImg(w, h, 4)){
      MessageBox(NULL, "Size() failed!", "Error LoadRAW()", MB_OK);
      fclose(img);
      return false;
   }

   unsigned int bytesPerLine   = mWidth * 4;         // Get the number of bytes per line

   // Loop through and read the data in
   for(int i=0; i<mHeight; i++){
      /*
         i is the line number in the image we are on. In this case, i
            will start at 0 being the top of the file. From each line, we need
            to relocate our position
      */
      unsigned char* pos = GetImageData(i);         // Relocate our position

      /*
         Read in the image data
      */
      if(!fread(pos, bytesPerLine, 1, img)){
         MessageBox(NULL, "fread() at ImageData failed!", "Error LoadRAW()", MB_OK);
         fclose(img);
         return false;
      }
   }

   // Loaded the image successfully, we now just do what we want with the image data
   fclose(img);
   return true;
} 